package com.atguigu.gmall.item.services.impl;

import com.atguigu.gmall.cache.annotation.MallCache;
import com.atguigu.gmall.cache.service.CacheOpsService;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.feign.product.SkuDetailFeignClient;
import com.atguigu.gmall.feign.search.SearchFeignClient;
import com.atguigu.gmall.item.services.SkuDetailService;
import com.atguigu.gmall.product.entity.SkuImage;
import com.atguigu.gmall.product.entity.SkuInfo;
import com.atguigu.gmall.product.entity.SpuSaleAttr;
import com.atguigu.gmall.web.SkuDetailVo;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

@Service
@Slf4j
public class SkuDetailServiceImpl implements SkuDetailService {

    @Autowired
    SkuDetailFeignClient skuDetailFeignClient;
    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    CacheOpsService cacheOps;
    @Autowired
    RedissonClient redisson;
    @Autowired
    ThreadPoolExecutor poolExecutor;
    @Autowired
    SearchFeignClient searchFeignClient;

    ReentrantLock lock = new ReentrantLock();

    RBloomFilter<Object> skuIdBloom;

    //这个组件创建好以后会执行这个方法
    @PostConstruct
    void init() {
//        log.info("项目启动：正在初始化布隆过滤器");
//        //分布式布隆过滤器
//        skuIdBloom = redisson.getBloomFilter(RedisConst.BLOOM_SKUID);
//        //1、初始化布隆
//        if(!skuIdBloom.isExists()){
//            //如果不存在
//            skuIdBloom.tryInit(1000000,0.0000001);
//            //2、添加数据到布隆
//            //获取到所有skuId；
//            Result<List<Long>> skuId = skuDetailFeignClient.getAllSkuId();
//            skuId.getData().forEach(item->{
//                skuIdBloom.add(item);
//            });
//        }
//        log.info("布隆过滤器初始化完成... 49:{} 77:{}",skuIdBloom.contains(49L),skuIdBloom.contains(77L));
//
//
//        this.bloomFilter = BloomFilter.create(Funnels.longFunnel(), 5000000, 0.0000001);
//
//        skuId.getData().forEach(item -> {
//            this.bloomFilter.put(item);
//        });
//
//        log.info("布隆过滤器初始化完成... 49:{} 77:{}", this.bloomFilter.mightContain(49L), this.bloomFilter.mightContain(77L));
        log.info("项目启动：正在初始化布隆过滤器");
        //分布式布隆过滤器
        skuIdBloom = redisson.getBloomFilter(RedisConst.BLOOM_SKUID);
        //1、初始化布隆
        if (!skuIdBloom.isExists()) {
            //如果不存在
            skuIdBloom.tryInit(1000000, 0.0000001);
            //2、添加数据到布隆
            //获取到所有skuId；
            Result<List<Long>> skuId = skuDetailFeignClient.getAllSkuId();
            skuId.getData().forEach(item -> {
                skuIdBloom.add(item);
            });
        }
        log.info("布隆过滤器初始化完成... 49:{} 77:{}", skuIdBloom.contains(49L), skuIdBloom.contains(77L));

    }

    @MallCache(
            cacheKey = RedisConst.SKU_DETAIL_CACHE_PREFIX + "#{#args[0]}",
            bloomKey = RedisConst.BLOOM_SKUID,
            bloomValue = "#{#args[0]}",
            ttl = 10L,
            unit = TimeUnit.MINUTES)
    @Override
    public SkuDetailVo skuDetail(Long skuId) {
        SkuDetailVo result = getSkuDetailFromRpc(skuId);
        return result;
    }

    private SkuDetailVo getSkuDetailFromRpc(Long skuId) {
        SkuDetailVo data = new SkuDetailVo();

        CompletableFuture<SkuInfo> skuInfoFuture = CompletableFuture.supplyAsync(() -> {
            //1、查询出sku_info信息
            Result<SkuInfo> skuInfo = skuDetailFeignClient.getSkuInfo(skuId);
            SkuInfo info = skuInfo.getData();
            //设置好skuinfo的数据返回
            data.setSkuInfo(info);
            return info;
        }, poolExecutor);


        //2、查询当前sku的图片列表
        CompletableFuture<Void> imageFuture = skuInfoFuture.thenAcceptAsync(sku -> {
            Result<List<SkuImage>> skuImages = skuDetailFeignClient.getSkuImages(skuId);
            List<SkuImage> images = skuImages.getData();
            sku.setSkuImageList(images);
        }, poolExecutor);


        //3、查询sku的分类信息
        CompletableFuture<Void> categoryFuture = skuInfoFuture.thenAcceptAsync((sku) -> {
            Result<SkuDetailVo.CategoryView> categoryView = skuDetailFeignClient.getCategoryView(sku.getCategory3Id());
            data.setCategoryView(categoryView.getData());
        }, poolExecutor);


        CompletableFuture<Void> priceFuture = CompletableFuture.runAsync(() -> {
            //4、查询sku的价格信息。为了得到最新价格，每次都再查一遍
            Result<BigDecimal> skuPrice = skuDetailFeignClient.getSkuPrice(skuId);
            data.setPrice(skuPrice.getData());
        }, poolExecutor);

        //5、查询sku的销售属性
        CompletableFuture<Void> saleAttrFuture = skuInfoFuture.thenAcceptAsync(res -> {
            Long spuId = res.getSpuId();
            Result<List<SpuSaleAttr>> value = skuDetailFeignClient.getSpuSaleAttrAndValue(spuId, skuId);
            data.setSpuSaleAttrList(value.getData());
        }, poolExecutor);


        //6、json 就是 map（"119|120":49,"122:123":50）、javaBean
        CompletableFuture<Void> valueJsonFuture = skuInfoFuture.thenAcceptAsync(res -> {
            Result<String> skuJson = skuDetailFeignClient.getSpuValuesSkuJson(res.getSpuId());
            data.setValuesSkuJson(skuJson.getData());
        }, poolExecutor);

        //以上任务全部运行结束
        CompletableFuture.allOf(imageFuture, categoryFuture, priceFuture, saleAttrFuture, valueJsonFuture).join();

        return data;
    }

    @Override
    public void incrHotScore(Long skuId) {
        //累积更新。skuId 每满 100 给 es 更一次  hotscore:49
        //返回最新叠加到的值
        Long increment = redisTemplate.opsForValue().increment(RedisConst.HOTSCORE + skuId);
        if(increment % 100 == 0){
            //远程让es更新 skuId=increment
            searchFeignClient.updateHotScore(skuId,increment);
        }
    }
}
